import { model } from '@/ts/base';
import { LoadOptions, LoadResult } from '@/ts/base/model';
import { XThing } from '@/ts/base/schema';
import { ReportTree } from '../reporttree/ReportTree';
import { IBaseView, BaseView } from './baseView';
import { convertToMongoMatch, convertToMongoSort, convertToMongoSummary } from '@/utils';
import _ from 'lodash';

export interface IFormView extends IBaseView {
  allowView: boolean;
  _treeLoaded: boolean;
  _summaryLoaded: boolean;
  summary: any;
  /** 加载组织菜单 */
  _speciesLoaded: boolean;
  //成员树
  memberTree: any[];
  /** 组织树 */
  organizationTree: any[];
  /** 加载组织菜单 */
  loadNodeTree(): Promise<any[]>;
  /** 加载分类菜单 */
  loadSpeciesMenu(): Promise<any[]>;
  /** 查询表数据 */
  loadThing(loadOptions: LoadOptions<XThing>): Promise<LoadResult<XThing[]>>;
  /** 创建查询索引 */
  createIndex(idxField: string): Promise<void>;
}
/** 表单视图 */
export class FormView extends BaseView implements IFormView {
  _treeLoaded: boolean = false;
  _summaryLoaded: boolean = false;
  _lastId: string = '';
  queryLoadOptions: LoadOptions<XThing> = {};
  summary: any = {};
  memberTree: any[] = [];
  organizationTree: any[] = [];
  _memberTreeLoaded = false;
  _speciesLoaded = false;
  get spaceId(): string {
    return this.directory.spaceId;
  }
  get treeId(): string {
    return this.metadata.options?.organizationTree || '';
  }
  get allowView(): boolean {
    return (
      this.directory.target.belongId !== this.spaceId &&
      this.metadata.options?.allowMemberView !== true
    );
  }

  override async loadContent() {
    await this.load();
    await this.loadFields();
    return true;
  }

  async loadNodeTree(reload = false) {
    if (!this._treeLoaded || reload) {
      const treeInfo = await this.directory.resource.reportTreeColl.find([this.treeId!]);
      if (Array.isArray(treeInfo) && treeInfo.length > 0) {
        const _field = treeInfo[0];
        const treeCurrent = new ReportTree(_field, this.directory);
        let nodes = await treeCurrent.loadNodes();
        const root = nodes.find((n) => {
          if (_field.belongId == this.spaceId) {
            return n.id == _field.rootNodeId;
          }
          return n.targetId == this.spaceId;
        });
        if (!root) return [];
        nodes.forEach((n: any) => {
          n['rootCode'] = root.code;
        });
        this.organizationTree = await treeCurrent.loadSubTree(root);
      }
      this._treeLoaded = true;
    }

    return this.organizationTree;
  }
  //加载集群成员/单位组织 树
  async loadMemberTree() {
    if (this.isGroupSpace) {
      //加载 成员
      console.log('加载成员');
    }
    //成员树
    return [];
  }
  async loadSpeciesMenu() {
    const result = [];
    let treeNodes = [];
    this.changCallback('view', 'treeLoading', true);
    if (this.treeId) {
      treeNodes = await this.loadNodeTree();
    } else {
      treeNodes = await this.loadMemberTree();
    }
    treeNodes.length > 0 && result.push(...treeNodes);

    for (const filed of this.fields.filter((i) => i.options?.species)) {
      const rootCode = filed.options?.viewFilterKey || filed.code;
      let item = {
        ...filed,
        rootCode,
        checkable: false,
        children: [],
        typeName: '分类项',
      };
      this.loopLookups(item, filed.lookups ?? []);
      result.push(item);
    }
    this.changCallback('view', 'treeLoading', false);
    return result;
  }
  /** 递归加载分类项 */
  loopLookups(node: any, lookups: model.FiledLookup[], pid?: string) {
    const children = lookups.filter((i) => i.parentId == (pid ? pid : void 0));
    if (children.length > 0) {
      node.children = children.map((item) => ({
        rootCode: node.rootCode,
        name: item.text,
        id: item.id,
        checkable: false,
        parentId: pid,
        value: item.value,
        children: [],
        info: item.info,
        typeName: '分类项',
      }));
      node.children.forEach((i: any) => this.loopLookups(i, lookups, i.id));
    }
  }

  override async loadThing(loadOptions: LoadOptions): Promise<LoadResult<any>> {
    loadOptions = loadOptions || {};
    loadOptions.filter = this.parseFilter(loadOptions?.filter ?? []);
    loadOptions.options = loadOptions.options || {};
    loadOptions.options.match = loadOptions.options.match || {};
    loadOptions.options.match.isDeleted = false;
    loadOptions.options.match = { ...loadOptions.options.match, ...this.parseClassify() };
    //虚拟列
    if (this.fields.some((item) => item.id.includes('virtualColumn'))) {
      loadOptions.formId = this.id;
    }
    //办事查询
    let project = {};
    if (this.viewType === 'work') {
      project = { data: 0, records: 0 };
    } else if (!this._metadata.collName || this._metadata.collName === '_system-things') {
      project = { archives: 0 };
    }
    if (Object.keys(project).length > 0) {
      loadOptions.options = Object.assign(loadOptions.options, { project });
    }
    // 没有过滤条件，没有分类条件
    if (!loadOptions.filter?.length && Object.keys(this.parseClassify()).length === 0) {
      return { data: [], totalCount: 0 } as any;
    }
    const belongIds = loadOptions.options.match.belongId;
    const keys = ['_in_', '_all_'];
    // 是否使用聚合查询：belongId 数组长度大于 30
    const isAggregatedQuery =
      belongIds &&
      keys.some((key) => Array.isArray(belongIds[key]) && belongIds[key].length > 30);

    let res: any = {};
    // 普通查询
    if (!isAggregatedQuery) {
      res = await this.thingColl.loadResult(this.updataSort(loadOptions));
    } else {
      const newLoadOptions: any = _.cloneDeep(loadOptions);
      const newMatch = convertToMongoMatch(newLoadOptions.filter ?? []);
      const newSort = convertToMongoSort(newLoadOptions.sort ?? []);
      const newSummary = convertToMongoSummary(
        (newLoadOptions as any)?.totalSummary ?? [],
      );

      const baseMatch = {
        isDeleted: false,
        ...(newLoadOptions?.options?.match || {}),
        ...(newMatch || {}),
        labels:
          Array.isArray(newLoadOptions.userData) && newLoadOptions.userData.length > 0
            ? {
                _in_: newLoadOptions.userData,
              }
            : undefined,
      };
      const options = [
        {
          match: this._lastId ? { ...baseMatch, id: { _gt_: this._lastId } } : baseMatch,
        },
        { sort: { ...(newSort || {}) } },
        { project: { archives: 0 } },
        { limit: newLoadOptions.take || 20 },
      ];

      const data = await this.thingColl.loadAggregate(options);

      data &&
        data.forEach((item: any) => {
          item.id = item._id;
        });

      res['data'] = data ?? [];
      const summaryProject: any = {
        _id: 0,
        totalCount: 1,
      };

      Object.keys(newSummary).forEach((key) => {
        summaryProject[key] = 1;
      });

      const belongIdArray = belongIds['_in_'] || belongIds['_all_'];
      const chunkSize = 30;
      const queryType = '_in_' in belongIds ? '_in_' : '_all_';

      function removeIdGtCondition(options: any) {
        if (options?.match?.id) {
          options = _.cloneDeep(options);
          delete options.match.id;
        }
        return options;
      }

      const diffObj1 = {
        filter: this.queryLoadOptions.filter ?? [],
        options: removeIdGtCondition(this.queryLoadOptions.options ?? {}),
        userData: this.queryLoadOptions.userData ?? [],
        isExporting: this.queryLoadOptions.isExporting ?? false,
      };

      const diffObj2 = {
        filter: newLoadOptions.filter ?? [],
        options: removeIdGtCondition(newLoadOptions.options ?? {}),
        userData: newLoadOptions.userData ?? [],
        isExporting: newLoadOptions.isExporting ?? false,
      };

      // 查询条件是否相等
      const queryOptionsEqual = _.isEqual(_.sortBy(diffObj1), _.sortBy(diffObj2));
      if (!queryOptionsEqual) {
        this._lastId = '';
      }
      this.queryLoadOptions = newLoadOptions;

      if (!this._summaryLoaded || !queryOptionsEqual) {
        const chunkPromises = belongIdArray
          .reduce((acc: string[][], _: any, i: number) => {
            if (i % chunkSize === 0) {
              acc.push(belongIdArray.slice(i, i + chunkSize));
            }
            return acc;
          }, [] as string[][])
          .map(async (chunk: string[]) => {
            const summaryOptions = [
              {
                match: {
                  isDeleted: false,
                  ...(newLoadOptions?.options?.match || {}),
                  belongId: {
                    [queryType]: chunk,
                  },
                  labels:
                    Array.isArray(newLoadOptions.userData) &&
                    newLoadOptions.userData.length > 0
                      ? {
                          _in_: newLoadOptions.userData,
                        }
                      : undefined,
                  ...(newMatch || {}),
                },
              },
              {
                group: {
                  key: [],
                  ...(newSummary || {}),
                  totalCount: {
                    _sum_: 1,
                  },
                },
              },
              { project: summaryProject },
              { limit: 1 },
            ];
            return this.thingColl.loadAggregate(summaryOptions);
          });

        const results = await Promise.all(chunkPromises);
        function processData(data: any[], summaryFields: any[]) {
          if (!Array.isArray(data)) {
            return { totalCount: 0, summary: [] };
          }
          let totalCount = 0;
          let summary: any = {};
          if (Array.isArray(summaryFields)) {
            summaryFields.forEach((field) => {
              summary[field.selector] = 0;
            });
          }
          data.forEach((group) => {
            group &&
              group.forEach((item: any) => {
                totalCount += item.totalCount || 0;
                Array.isArray(summaryFields) &&
                  summaryFields.forEach((field) => {
                    summary[field.selector] += item[field.selector] || 0;
                  });
              });
          });
          return { totalCount, summary: Object.values(summary) };
        }

        const totals = processData(results, (newLoadOptions as any).totalSummary);
        res.totalCount = totals.totalCount;
        res.summary = totals.summary;
        this.summary = totals;
        this._summaryLoaded = true;
      } else {
        res.totalCount = this.summary.totalCount;
        res.summary = this.summary.summary;
      }
    }
    res.data = Array.isArray(res.data) ? res.data : [];
    res.totalCount = res.totalCount ?? 0;
    res.groupCount = res.groupCount ?? 0;
    if (res.data.length > 0) {
      this._lastId = res.data[res.data.length - 1].id;
    }
    return res;
  }

  async createIndex(idxField: string): Promise<void> {
    await this.thingColl.createIndex([
      {
        name: idxField,
        key: {
          [idxField]: 1,
        },
      },
    ]);
  }
}
